using System;
using System.Windows.Forms;
using System.Drawing;
using System.Drawing.Imaging;
using System.ComponentModel;

namespace Microsoft.Samples
{
	/// <summary>
	/// Summary description for BounceButton.
	/// </summary>
	public class BounceButton : StateButtonBase
	{
		#region Fields

		// Property fields
		bool autoSizeImage;
		float bounceMaxSize;
		float bounceRate;
		bool enableBounce;
		Image image;
		
		// used for image bouncing
		System.Windows.Forms.Timer timer;
		float percentGrowth;
		bool growing;

		// used for animation
		bool currentlyAnimating;

		#endregion

		#region Constructor
		public BounceButton()
		{
			// Set control style
			// Use double buffering and set the transparency support
			this.SetStyle(ControlStyles.ResizeRedraw 
				| ControlStyles.DoubleBuffer 
				| ControlStyles.SupportsTransparentBackColor, true);

			timer = new System.Windows.Forms.Timer();
			timer.Enabled = false;
			timer.Interval = 70;
			timer.Tick += new EventHandler(this.timerTick);

			// set up image bouncing defaults
			percentGrowth = 1.0f;
			growing = true;
			enableBounce = true;
			bounceMaxSize = 1.3f;
			bounceRate = .05f;

			// Default color is transparent
			this.BackColor = Color.Transparent;

			// Default animate state is false
			currentlyAnimating = false;

			// Default AutoSizeImage state is true
			autoSizeImage = true;
		}
		#endregion 

		#region Properties
		[
		Category("Appearance"),
		DefaultValue(true),
		Description("Determines whether the image is stretched to fit the size of the button")
		]
		public bool AutoSizeImage 
		{
			get 
			{
				return autoSizeImage;
			}
			set 
			{
				autoSizeImage = value;
				this.Invalidate();
			}
		}

		// TODO: make this work
		[
		Category("Appearance"),
		DefaultValue(typeof(System.Drawing.Color), "Color.Transparent")
		]
		public override Color BackColor 
		{
			get 
			{
				return base.BackColor;
			}
			set 
			{
				base.BackColor = value;
			}
		}

		[
		Category("Behavior"),
		DefaultValue(1.3f),
		Description("Specifies the multiplier for the maximum size the button will grow to when bouncing")
		]
		public float BounceMaxSize 
		{
			get 
			{
				return bounceMaxSize;
			}
			set 
			{
				if (value >= 1.0) 
				{
					bounceMaxSize = value;
				}
			}
		}

		[
		Category("Behavior"),
		DefaultValue(.05f),
		Description("Specifies the rate at which the size mulitplier grows when bouncing")
		]
		public float BounceRate 
		{
			get 
			{
				return bounceRate;
			}
			set 
			{
				if (value >= 0) 
				{
					bounceRate = value;
				}
			}
		}

		[
		Category("Behavior"),
		DefaultValue(true),
		Description("Specifies whether the image bounces when the mouse is over the button")
		]
		public bool EnableBounce 
		{
			get 
			{
				return enableBounce;
			}
			set 
			{
				enableBounce = value;
			}
		}

		[
		Category("Appearance"),
		DefaultValue(null),
		Description("The image displayed by the button")
		]
		public Image Image 
		{
			get 
			{
				return image;
			}
			set 
			{
				image = value;
				if (value == null) 
				{
					StopAnimate();
				}
				this.Invalidate();
			}
		}
		#endregion

		#region Methods and Event Handlers
		internal void Animate() 
		{
			Animate(Visible && Enabled);
		}

		private void Animate(bool animate) 
		{
			if (animate != currentlyAnimating) 
			{
				if (animate) 
				{
					if (image != null) 
					{
						ImageAnimator.Animate(image, new EventHandler(this.OnFrameChanged));
						currentlyAnimating = animate ? true : false;
					}
				}
				else 
				{
					if (image != null) 
					{
						ImageAnimator.StopAnimate(image, new EventHandler(this.OnFrameChanged));
						currentlyAnimating = animate ? true : false;
					}
				}
			}
		}

		public void DrawImageDisabled(Graphics graphics, Image image, int x, int y, int width, int height) 
		{
			Size imageSize = image.Size;
			ImageAttributes disabledImageAttr = null;

			if (disabledImageAttr == null) 
			{
				// This is how I came up with this somewhat random ColorMatrix.
				// Its set to resemble Office10 commandbars, but still be able to
				// deal with hi-color (256+) icons and images.
				//
				// The idea is to scale everything down (more than just a grayscale does,
				// therefore the small numbers in the scaling part of matrix)
				// White -> some shade of gray &
				// Black -> Black
				//
				// Second part of the matrix is to translate everything, so all colors are
				// a bit brigher.
				// Grays become lighter and washed out looking
				// Black becomes a shade of gray as well.
				//
				// btw, if you do come up with something better let me know - nikhilko
                
				float[][] array = new float[5][];
				array[0] = new float[5] {0.2125f, 0.2125f, 0.2125f, 0, 0};
				array[1] = new float[5] {0.2577f, 0.2577f, 0.2577f, 0, 0};
				array[2] = new float[5] {0.0361f, 0.0361f, 0.0361f, 0, 0};
				array[3] = new float[5] {0,       0,       0,       1, 0};
				array[4] = new float[5] {0.38f,   0.38f,   0.38f,   0, 1};

				ColorMatrix grayMatrix = new ColorMatrix(array);

				disabledImageAttr = new ImageAttributes();
				disabledImageAttr.ClearColorKey();
				disabledImageAttr.SetColorMatrix(grayMatrix);
			}

			graphics.DrawImage(image, 
				new Rectangle(x, y, width, height), 
				0, 0, imageSize.Width, imageSize.Height,
				GraphicsUnit.Pixel, 
				disabledImageAttr);
		}

		private void OnFrameChanged(object o, EventArgs e) 
		{
			// TODO: how can I get access to this private member?
			//			if (IsWindowObscured) 
			//			{
			//				StopAnimate();
			//				return;
			//			}
			Invalidate();  
		}

		protected override void OnLocationChanged(EventArgs e)
		{
			base.OnLocationChanged(e);
			this.Invalidate();
		}

		protected override void OnMouseDown(MouseEventArgs e) 
		{
			base.OnMouseDown(e);
			ProcessState();
		}

		protected override void OnMouseEnter(EventArgs e) 
		{
			base.OnMouseEnter(e);
			ProcessState();
		}

		protected override void OnMouseLeave(EventArgs e) 
		{
			base.OnMouseLeave(e);
			ProcessState();
		}

		protected override void OnMouseUp(MouseEventArgs e) 
		{
			base.OnMouseUp(e);
			ProcessState();
		}

		protected override void OnPaint(PaintEventArgs ev) 
		{
			// TODO???
			Animate();
			ImageAnimator.UpdateFrames();
			
			// Paint Background
			InvokePaintBackground(this, new PaintEventArgs(ev.Graphics, ClientRectangle));
			
			// Calculate image size, take AutoSizeImage property into account
			RectangleF imageRect;
			if (AutoSizeImage) 
			{
				imageRect = new RectangleF((float)((this.ClientSize.Width - this.ClientSize.Width * (1 / BounceMaxSize)) / 2.0), (float)((this.ClientSize.Height - this.ClientSize.Height * (1 / BounceMaxSize)) / 2.0f), (float)(this.ClientSize.Width * (1 / BounceMaxSize)), (float)(this.ClientSize.Height * (1 / BounceMaxSize)));
			}
			else 
			{
				imageRect = new RectangleF((float)((this.Width - image.Width) / 2), (float)((this.Height - image.Height) / 2), (float)image.Width, (float)image.Height);
			}

			// Paint Image
			if (this.image != null) 
			{
				// TODO: I want to be able to index into icons and get the largest sized one
				if (Enabled) 
				{
					imageRect.Offset(-((imageRect.Width * percentGrowth) - imageRect.Width) / 2, -((imageRect.Height * percentGrowth) - imageRect.Height) / 2);
					imageRect.Width = imageRect.Width * percentGrowth;
					imageRect.Height = imageRect.Height * percentGrowth;
					ev.Graphics.DrawImage(this.image, imageRect);
				}
				else 
				{
					DrawImageDisabled(ev.Graphics, this.image, (int)imageRect.X, (int)imageRect.Y, (int)imageRect.Width, (int)imageRect.Height);
				}
			}
			base.OnPaint(ev);
		}
		
		private void ProcessState() 
		{
			if ((State & StateButtonState.Pressed) != 0) 
			{
				if (EnableBounce) 
				{
					timer.Enabled = false;
				}
				this.percentGrowth = .8f;
			}
			else if ((State & StateButtonState.MouseHover) != 0) 
			{
				if (EnableBounce) 
				{
					timer.Enabled = true;
					growing = true;
				}
				percentGrowth = 1.0f;
			}
			else 
			{
				if (EnableBounce) 
				{
					timer.Enabled = false;
				}
				percentGrowth = 1.0f;
			}
			this.Invalidate();
		}

		// timer used to support button bouncing
		private void timerTick(Object sender, EventArgs e) 
		{
			if (growing) 
			{
				percentGrowth += BounceRate;
				if (percentGrowth > BounceMaxSize) 
				{
					percentGrowth = BounceMaxSize;
					growing = false;
				}
			}
			else 
			{
				percentGrowth -= BounceRate;
				if (percentGrowth < 1.0) 
				{
					percentGrowth = 1.0f;
					growing = true;
				}
			}
			this.Invalidate();
		}

		// Used to support animated images
		internal void StopAnimate() 
		{
			Animate(false);
		}
		#endregion
	}
	
}
